/**
 * 
 */
package spatial.geometry;

import java.awt.geom.Point2D;

/**
 * @author wu wei
 * 
 */
public class Intersection extends Point2D.Double {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5992089838951104176L;
	protected LineFunction l1, l2;

	/**
	 * If the given two lines are parallel, a NotDefinedException will be
	 * thrown. If do not want use try-catch clause, then use the static method
	 * getIntersectionPoint to construct an intersection point.
	 * getIntersectionPoint will return a null when the given two lines are
	 * parallel.
	 * 
	 * @param l1
	 * @param l2
	 */
	public Intersection(LineFunction l1, LineFunction l2) {
		super();
		Point2D.Double p = pointOfIntersection(l1, l2);
		if (p == null) {
			throw new NotDefinedException();
		} else {
			this.setLocation(p);
			this.l1 = l1;
			this.l2 = l2;
		}
	}

	/**
	 * Compute the intersection of the two given lines.
	 * 
	 * @param l1
	 * @param l2
	 * @return null if the two given lines does not intersect; their
	 *         intersection otherwise.
	 */
	public static Intersection compIntersection(LineFunction l1, LineFunction l2) {
		/**
		 * <pre>
		 * compute the intersection point (x,y) of the given two lines;
		 * use the private constructor to build a intersection point, and return it;		 * 
		 * </pre>
		 */
		Point2D.Double p = pointOfIntersection(l1, l2);
		if (p == null) {
			return null;
		} else {
			return new Intersection(l1, l2);
		}
	}

	public LineFunction getLine1() {
		return this.l1;
	}

	public LineFunction getLine2() {
		return this.l2;
	}

	/**
	 * check whether the given linefunction is a contributing line. A line is a
	 * contributing line if it is one of the two lines that make this
	 * intersection.
	 * 
	 * @param lf
	 * @return true if the given line is a contributing line; false otherwise.
	 */
	public boolean isAContributingLine(LineFunction lf) {
		return (this.l1 == lf || this.l2 == lf);
	}

	public String toString() {
		// String s = ((Point2D.Double) this).toString();
		String s = super.toString();
		s = s + ", " + l1.toString() + ", " + l2.toString();
		return s;
	}

	@Override
	public boolean equals(Object obj) {
		if (super.equals(obj)) {
			if (obj instanceof Intersection) {
				Intersection objI = (Intersection) obj;
				return (this.l1.equals(objI.l1) && this.l2.equals(objI.l2));
			}
		}
		return false;
	}

	private static Point2D.Double pointOfIntersection(LineFunction l1,
			LineFunction l2) {
		LineType l1Type = l1.getLineType();
		LineType l2Type = l2.getLineType();
		double x = 0;
		double y = 0;
		if (l1Type == LineType.XPARALLEL) {
			if (l2Type == LineType.XPARALLEL) {
				return null;
			}
			if (l2Type == LineType.YPARALLEL) {
				x = l2.getC() * -1;
				y = l1.getC();
			}
			if (l2Type == LineType.NORMAL) {
				y = l1.getC();
				x = l2.getXByY(y);
			}

		}

		if (l1Type == LineType.YPARALLEL) {
			if (l2Type == LineType.XPARALLEL) {
				x = l1.getC() * -1;
				y = l2.getC();
			}
			if (l2Type == LineType.YPARALLEL) {
				return null;
			}
			if (l2Type == LineType.NORMAL) {
				x = l1.getC() * -1;
				y = l2.getYByX(x);
			}
		}

		if (l1Type == LineType.NORMAL) {
			if (l2Type == LineType.XPARALLEL) {
				y = l2.getC();
				x = l1.getXByY(y);
			}
			if (l2Type == LineType.YPARALLEL) {
				x = l2.getC() * -1;
				y = l1.getYByX(x);
			}
			if (l2Type == LineType.NORMAL) {
				if (l1.getA() == l2.getA()) {
					// parallel
					return null;
				} else {
					// COMPUTE x = (c2-c1)/(a1-a2), y = ax+c
					x = (l1.getC() - l2.getC()) / (l2.getA() - l1.getA());
					y = l1.getYByX(x);
				}
			}
		}

		return new Point2D.Double(x, y);
	}

}
